/*
 * Fall 2008: Computer Graphics B581
 * lighting template code
 */

#include <stdio.h>
#include <stdlib.h>
// #include <unistd.h>
#include <math.h>

#ifdef WIN32
#include <windows.h>
#endif

#if defined (__APPLE__) || defined(MACOSX)
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif

#include "readoff.h"
#include "readppm.h"

/*****************************************************************/
/******************** MY FUNCTION DEFINITIONS ********************/
/*****************************************************************/

static void myInitGL(void);
static void myInitData(int *argc, char **argv);
static void myInitMenus(void);
static void myInitCheckers(void);
static void myDrawTeapot(GLdouble size);

/* my callback functions needed for GLUT event handling */
static void myHandleMenu(int value);
static void myDisplay(void);
static void myReshape(int width, int height);
static void myMotion(int curx, int cury);
static void myButton(int button, int state, int x, int y);
static void myVisibility(int state);
static void myKeyboard(unsigned char key, int x, int y);
static void myMenus(int value);

/***************************************************************/
/******************** MY GLOBAL CONSTANTS **********************/
/***************************************************************/

#define MY_MAX(A,B) ((A) >= (B) ? (A) : (B))

/* my constants for GLUT window defaults */
#define MY_WIND_X_SIZE     600
#define MY_WIND_Y_SIZE     600
#define MY_WIND_X_POS      100
#define MY_WIND_Y_POS      100

/* my menu IDs for GLUT menu-handling functions */
#define MY_MENU_EXIT     -9999
#define MY_MENU_RESET     -200
#define MY_MENU_CAMERA      20
#define MY_MENU_LIGHT       30
#define MY_MENU_OBJECT      40
#define MY_MENU_PROJECT     100

#define	MY_CHECKERS_WIDTH   64
#define	MY_CHECKERS_HEIGHT  64

/***************************************************************/
/******************** MY GLOBAL VARIABLES **********************/
/***************************************************************/

/* globals for generated texture - modified from Red Book's original checker.c - */
static GLubyte CheckersImg[MY_CHECKERS_HEIGHT][MY_CHECKERS_WIDTH][3];
static GLuint TextureName;
static GLboolean TextureOn= GL_TRUE;

static TexMap TMap; /* global for texture from readppm */

static GLfloat LightPos[4];

/* user interaction */
static GLint InteractionMode;
static GLint XmouseDown, YmouseDown;
static GLint XmouseCurrent, YmouseCurrent;
static GLint XmousePrev, YmousePrev, DX, DY;
static GLint WindHeight, WindWidth;
static GLint KeybModifiers;
static GLint mouseButtons = 0;

/* transformations */
static GLdouble TX, TY, TZ;
GLfloat mat[16];

/* 3D Projection */
static GLdouble XCamera, YCamera, ZCamera;
static GLdouble FOVangle, FOVratio, ClipNear, ClipFar;

/* OFF files */
Object3D my3dObject;
Point3 *faceNormals;

Vector3 constructVector(HPoint3 start, HPoint3 end) {
	Vector3 newVector;
	newVector.x = end.x - start.x;
	newVector.y = end.y - start.y;
	newVector.z = end.z - start.z;
	return newVector;
}

Vector3 performCrossProduct(Vector3 v1, Vector3 v2) {
	Vector3 v;
	v.x = v1.y * v2.z - v1.z * v2.y;
	v.y = v1.z * v2.x - v1.x * v2.z;
	v.z = v1.x * v2.y - v1.y * v2.x;
	return v;
}

Vector3 normalizeVector(Vector3 inVec) {
	Vector3 outVec;

	double reciprocal = sqrt(pow(inVec.x, 2) + pow(inVec.y, 2)
			+ pow(inVec.z, 2));
	outVec.x = inVec.x / reciprocal;
	outVec.y = inVec.y / reciprocal;
	outVec.z = inVec.z / reciprocal;
	return outVec;

}

Vector3 createFaceNormal(int faceIndex) {
	HPoint3 point_1 = my3dObject.vertices[my3dObject.faces[faceIndex][0]];
	HPoint3 point_2 = my3dObject.vertices[my3dObject.faces[faceIndex][1]];
	HPoint3 point_3 = my3dObject.vertices[my3dObject.faces[faceIndex][2]];

	Vector3 vec1, vec2, returnVec;

	// get two vectors
	vec1 = constructVector(point_1, point_2);
	vec2 = constructVector(point_1, point_3);

	// perform the cross product to get the normal vector
	returnVec = performCrossProduct(vec1, vec2);

	// normalize the normal vector
	return normalizeVector(returnVec);

}

/* --------------------------------------- */
/*           Load Off File in to           */
/*           Internal Data Structure       */
/* --------------------------------------- */

void loadOffFile(int *argc, char **argv) {

	if (*argc >= 2) {
		printf("Processing the OFF file %s.\n", argv[1]);
		ReadOFF(argv[1], &my3dObject);
	} else {
		printf("No OFF file given. Using the default cube OFF file.\n");
		ReadOFF("cube.off", &my3dObject);
	}

	// initialize face normal array to contain required number of normals.
	int numberOfFaces = my3dObject.Nfaces;
	faceNormals = (Point3 *) malloc(my3dObject.Nfaces*sizeof(Point3));;

	// now for each face calculate the normals
	int index = 0;
	for (index = 0; index < numberOfFaces; index++) {
		faceNormals[index] = createFaceNormal(index);
	}

}

/**************************** MAIN *****************************/

int main(int argc, char **argv) {
	glutInit(&argc, argv);

	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
	glutInitWindowPosition(MY_WIND_X_POS, MY_WIND_Y_POS);
	glutInitWindowSize(MY_WIND_X_SIZE, MY_WIND_Y_SIZE);

	glutCreateWindow("B581 template code with 3D lighting");

	glutDisplayFunc(myDisplay);
	glutReshapeFunc(myReshape);
	glutMouseFunc(myButton);
	glutMotionFunc(myMotion);
	glutPassiveMotionFunc(NULL);
	glutIdleFunc(NULL);
	glutKeyboardFunc(myKeyboard);
	glutVisibilityFunc(myVisibility);

	fprintf(stderr,"Press ESC or 'q' to Exit.\n");

	myInitGL();
	myInitData(&argc, argv); /* pass command line arguments for texture */

	/* initialize GLUT menu handler as last one, to take over access
	 to GLUT_RIGHT_BUTTON, regardles to what other handlers did to it: */
	myInitMenus();

	glutMainLoop();
	return (EXIT_SUCCESS);
} /* main() */

/* --------------------------------------- */
/*           initialize my data            */
/* --------------------------------------- */

void myInitData(int *argc, char **argv) {
	int i;
	char s[1024];
	InteractionMode = MY_MENU_CAMERA;

	TX = 1.1;
	TY = 0.8;
	TZ = -2.0;

	XmouseDown = -1;
	YmouseDown = -1;
	XmouseCurrent = XmousePrev = -1;
	YmouseCurrent = YmousePrev = -1;
	DX = DY = 0;
	KeybModifiers = 0;

	loadOffFile(argc, argv);

	/* initialize texturing */
	TextureOn = GL_TRUE;

	/* read texture from PPM file - printout command line arguments for debugging */
	fprintf(stderr,"argc = %d: ",*argc);
	for (i = 0; i< *argc; i++)
		fprintf(stderr, " arg[%d] = %s, ",i, argv[i]);
	fprintf(stderr,"\n");

	i = -1;
	if (*argc == 3) {
		/* load image from URL specified on command line */
		sprintf(s, "./getppm.pl %s > temp.ppm", argv[2]);
		printf("Executing:\n   %s\n", s);
		system(s);
		i = ReadPPM("temp.ppm", &TMap);
	}
//	} else if (*argc == 2) {
//		/* load image from default URL */
//		printf("Using default texture file 'fermat_raw.ppm'.\n");
//		system("./getppm.pl http://www.cs.indiana.edu/~mitja/Delo/B581/fermat_raw.ppm > temp.ppm");
//		i = ReadPPM("temp.ppm", &TMap);
//	}
	if (i > 0) {
		printf("Using texture from file.\n"); /* image file is valid: use it as texture */
		glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
		glGenTextures(1, &TextureName);
		glBindTexture(GL_TEXTURE_2D, TextureName);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, TMap.texc, TMap.texr,
		0, GL_RGB, GL_UNSIGNED_BYTE, TMap.texture);
	} else {
		printf("Using checkers texture.\n"); /* image file is invalid: use checkers */
		myInitCheckers();
		glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
		glGenTextures(1, &TextureName);
		glBindTexture(GL_TEXTURE_2D, TextureName);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, MY_CHECKERS_WIDTH, MY_CHECKERS_HEIGHT,
		0, GL_RGB, GL_UNSIGNED_BYTE, CheckersImg);
	}
} /* myInitData() */

/* --------------------------------------- */

void myInitCheckers(void) {
	int i, j, c;

	for (i = 0; i < MY_CHECKERS_HEIGHT; i++) {
		for (j = 0; j < MY_CHECKERS_WIDTH; j++) {
			c = ((((i&0x8)==0)^((j&0x8))==0))*255;
			CheckersImg[i][j][0] = (GLubyte) c;
			CheckersImg[i][j][1] = (GLubyte) c;
			CheckersImg[i][j][2] = (GLubyte) c;
		}
	}
} /* myInitCheckers() */

/***************************************************************/
/*********************** MOUSE CALLBACKS ***********************/
/***************************************************************/

/* ------------------------------------------------------------------------ */
/* Mouse Button  UP/DOWN  or DOWN/UP transition only */
/* ------------------------------------------------------------------------ */
void myButton(int button, int state, int x, int y) {

	XmouseDown = x;
	YmouseDown = WindHeight - y - 1; /* Invert to right-handed system */

	/* This permits detection of left, middle, left+middle */
	if (state == GLUT_DOWN)
		mouseButtons |= button;
	else
		mouseButtons &= (~(button));

	/* bitmask with GLUT_ACTIVE_SHIFT, GLUT_ACTIVE_CTRL, GLUT_ACTIVE_ALT */
	KeybModifiers = glutGetModifiers();

	if (button == GLUT_LEFT_BUTTON || button == GLUT_MIDDLE_BUTTON) {
		if (state == GLUT_DOWN) {
			XmouseCurrent = XmousePrev = XmouseDown;
			YmouseCurrent = YmousePrev = YmouseDown;
		} else {
			/* at button up, invalidate: */
			XmouseCurrent = XmousePrev = XmouseDown = -1;
			YmouseCurrent = YmousePrev = YmouseDown = -1;
			KeybModifiers = 0;
		}
		DX = DY = 0;
	}
	glutPostRedisplay();
} /* myButton() */

/* ------------------------------------------------------------------------ */
/* Mouse motion WHILE BUTTON DOWN  */
/*  COMPLAINS  if you call glutGetModifiers()  here!! */
/* ------------------------------------------------------------------------ */
void myMotion(int x, int y) {
	XmouseCurrent = x;
	YmouseCurrent = WindHeight - y - 1; /* Invert to right-handed system */

	DX = XmouseCurrent - XmousePrev;
	DY = YmouseCurrent - YmousePrev;
	// printf("%d %d \n", DX, DY);

	XmousePrev = XmouseCurrent;
	YmousePrev = YmouseCurrent;

	switch (mouseButtons) {
	case 0x1: /* Left Mouse Button   */
		break;
	case 0x2: /* Middle Mouse Button */
		break;
	case 0x3: /* Left+Middle Mouse Button */
		break;
	case 0x4: /* Right Mouse Button (disabled with Menu)  */
		break;
	}

	glutPostRedisplay();
} /* myMotion(int x, int y) */

/* ------------------------------------------------------------------------ */
/* Handle Keyboard */
/* ------------------------------------------------------------------------ */
void myKeyboard(unsigned char key, int x, int y) {
	/*  Option: To force all lower case */
	if (key >= 'A' && key <= 'Z')
		key = (key-'A') + 'a';

	switch (key) {
	case 'h':
		fprintf(stderr,"Help: \n\
  ESC or q: quit \n\
  t: toggle texture\n\
");
		break;
	case 't':
		if (TextureOn==GL_TRUE)
			TextureOn=GL_FALSE;
		else
			TextureOn=GL_TRUE;
		break;
	case 'q':
	case 27: /* 27 -> ESC */
		fprintf(stderr,"Normal Exit.\n");
		exit(EXIT_SUCCESS);
		break;

	default:
		fprintf(stderr,"Unhandled key: %c [%d] \n",key,key);
	}

	glutPostRedisplay();
} /* myKeyboard() */

/* -------------------------------------------------------------- */
/* myMenus() - callback for GLUT menus */
/* -------------------------------------------------------------- */
void myMenus(int value) {
	switch (value) {
	case MY_MENU_RESET:
		/* reset things here */
		break;
	case MY_MENU_CAMERA:
		InteractionMode = MY_MENU_CAMERA;
		break;
	case MY_MENU_LIGHT:
		InteractionMode = MY_MENU_LIGHT;
		break;
	case MY_MENU_OBJECT:
		InteractionMode = MY_MENU_OBJECT;
		break;
	case MY_MENU_PROJECT:
		InteractionMode = MY_MENU_PROJECT;
		break;
	case MY_MENU_EXIT:
		exit(0);
		break;
	default:
		//            fprintf(stderr,"unknown menu entry");
		;
	} /* switch () */
	glutPostRedisplay(); /* make sure we Display according to changes */
} /* myMenus() */

/* -------------------------------------------------------------- */
/* initialize GLUT menus */
/* -------------------------------------------------------------- */
void myInitMenus(void) {
	glutCreateMenu(myMenus);

	glutAddMenuEntry("Reset Everything", MY_MENU_RESET);
	glutAddMenuEntry(" ", -1);
	glutAddMenuEntry("Move Camera", MY_MENU_CAMERA);
	glutAddMenuEntry("Move Light", MY_MENU_LIGHT);
	glutAddMenuEntry("Move Object", MY_MENU_OBJECT);
	glutAddMenuEntry(" ", -2);
	glutAddMenuEntry("Project Test", MY_MENU_PROJECT);
	glutAddMenuEntry(" ", -3);
	glutAddMenuEntry("Exit", MY_MENU_EXIT);

	glutAttachMenu(GLUT_RIGHT_BUTTON);
} /* myInitMenus() */

/***************************************************************/
/************************** INIT GL ****************************/
/***************************************************************/

void myInitGL(void) {
	GLfloat light_ambient[] = { 0.1, 0.1, 0.1, 1.0 };
	GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
	GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };

	/* projection pipeline initialization */
	FOVangle = 60.0;
	FOVratio = 1.0;
	ClipNear = 0.1;
	ClipFar = 1000.0;
	XCamera = YCamera = 0.7;
	ZCamera = 5.0;

	WindWidth = glutGet(GLUT_WINDOW_WIDTH);
	WindHeight = glutGet(GLUT_WINDOW_HEIGHT);
	glViewport(0, 0, WindWidth, WindHeight);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	FOVratio = (float)WindWidth / (float)WindHeight; /* keep viewed x/y ratio == 1 */
	/* instead of glFrustum(-1.0*ratio, 1.0*ratio, -1.0,1.0, FLength, 25.0);  */
	gluPerspective(FOVangle, FOVratio, ClipNear, ClipFar);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	/* Colors, Lighting, Material  initialization */
	glClearColor( 0.4f, 0.6f, 0.5f, 1.0f);
	glPointSize(1);
	glEnable(GL_POINT_SMOOTH);
	glLineWidth(1);
	glEnable(GL_LINE_SMOOTH);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	/* we do **not** want model materials to follow the current GL color: */
	glDisable(GL_COLOR_MATERIAL);

	glShadeModel(GL_SMOOTH); /* or GL_FLAT */
	glEnable(GL_NORMALIZE);
	glDepthFunc(GL_LESS);
	glEnable(GL_DEPTH_TEST);
	glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
	LightPos[0] = -1.0;
	LightPos[1] = 2.0;
	LightPos[2] = 0.5;
	LightPos[3] = 1.0;
	glLightfv(GL_LIGHT0, GL_POSITION, LightPos);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
} /* myInitGL() */

/***************************************************************/
/********************** DRAW FUNCTIONS *************************/
/***************************************************************/

void myDrawTeapot(GLdouble size) {
	GLfloat materialDiffuse[] = { 0.7, 0.4, 0.2, 1.0 };

	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, materialDiffuse);

	if (TextureOn==GL_TRUE) {
		glEnable(GL_TEXTURE_2D);
		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
		glBindTexture(GL_TEXTURE_2D, TextureName);
		glFrontFace(GL_CW);
		glutSolidTeapot(size);
		glFrontFace(GL_CCW);
		glDisable(GL_TEXTURE_2D);
	} else {
		glFrontFace(GL_CW);
		glutSolidTeapot(size);
		glFrontFace(GL_CCW);
	}
} /* myDrawTeapot() */

void drawCordinateSystemOrigin() {
	glLineWidth(2.0);
	glBegin(GL_LINES);
	glColor3f(1.0, 0.0, 0.0);
	glVertex3f(0.0, 0.0, 0.0);
	glVertex3f(1.0, 0.0, 0.0);
	glEnd();
	glBegin(GL_LINES);
	glColor3f(0.0, 1.0, 0.0);
	glVertex3f(0.0, 0.0, 0.0);
	glVertex3f(0.0, 1.0, 0.0);
	glEnd();
	glBegin(GL_LINES);
	glColor3f(0.0, 0.0, 1.0);
	glVertex3f(0.0, 0.0, 0.0);
	glVertex3f(0.0, 0.0, 1.0);
	glEnd();
}

void drawScene() {

	GLfloat materialDiffuse[] = { 0.7, 0.4, 0.2, 1.0 };

	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, materialDiffuse);

	glColor3f(1.0, 1.0, 1.0);

	int faceIndex;
	for (faceIndex = 0; faceIndex < my3dObject.Nfaces; faceIndex++) {

		// we will only apply the texture to the first face (given in the problem description)
		if (faceIndex == 0) {
			glEnable(GL_TEXTURE_2D);
			glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
			glBindTexture(GL_TEXTURE_2D, TextureName);
			glFrontFace(GL_CW);
		}

		// draw the face now
		//fprintf(stderr, "Face Normals %f, %f, %f\n", faceNormals[faceIndex].x, faceNormals[faceIndex].y, faceNormals[faceIndex].z);
		glNormal3f(faceNormals[faceIndex].x, faceNormals[faceIndex].y,
				faceNormals[faceIndex].z);

		int index;
		GLfloat x_cord = 0.0, y_cord = 0.0, z_cord = 0.0, x_cord_tot = 0.0,
				y_cord_tot = 0.0, z_cord_tot = 0.0;
		
		glColor3f(0.4, 0.8, 0.1);
		glBegin(GL_POLYGON);

		int numberOfVertices = my3dObject.nv_face[faceIndex];
		for (index = 0; index < numberOfVertices; index++) {
			x_cord = my3dObject.vertices[my3dObject.faces[faceIndex][index]].x;
			y_cord = my3dObject.vertices[my3dObject.faces[faceIndex][index]].y;
			z_cord = my3dObject.vertices[my3dObject.faces[faceIndex][index]].z;

			x_cord_tot += x_cord;
			y_cord_tot += y_cord;
			z_cord_tot += z_cord;

			// map the texture to our cordinates. 
			if (index == 0)
				glTexCoord2d(0, 0);
			else if (index == 1)
				glTexCoord2d(1, 0);
			else if (index == 2)
				glTexCoord2d(1, 1);
			else
				glTexCoord2d(0, 1);

			glVertex3d(x_cord, y_cord, z_cord);

		}
		glEnd();

		if (faceIndex == 0) {
			glFrontFace(GL_CCW);
			glDisable(GL_TEXTURE_2D);
		}

		// now we need the center of this face to draw and show the normal to the face.
		// we will only draw the normal to ths face from the center of the face.

		// first find out the center of the face
		Point3 faceCenter;
		faceCenter.x = (double) x_cord_tot / numberOfVertices;
		faceCenter.y = (double) y_cord_tot / numberOfVertices;
		faceCenter.z = (double) z_cord_tot / numberOfVertices;

		// draw the normal
		glDisable(GL_LIGHTING);
		glLineWidth(4.0);
		glBegin(GL_LINES);
		glColor3f(0.1, 0.5, 0.8);
		glVertex3f(faceCenter.x, faceCenter.y, faceCenter.z);
		glVertex3f(faceCenter.x + faceNormals[faceIndex].x, faceCenter.y
				+ faceNormals[faceIndex].y, faceCenter.z
				+ faceNormals[faceIndex].z);
		glEnd();
		glEnable(GL_LIGHTING);
	}
}

/* ------------------------------------------------------------------------ */
void myDisplay(void) {
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glLoadIdentity();

	/* viewing transformation */
	if (InteractionMode == MY_MENU_CAMERA) {
		/* move camera */
		if ((KeybModifiers & GLUT_ACTIVE_SHIFT) == 0) {
			XCamera = XCamera + 0.01*(GLdouble)DX;
			YCamera = YCamera + 0.01*(GLdouble)DY;
		}
	}
	// one could also try different camera positioning techinques with:
	//   gluLookAt(XCamera, YCamera, ZCamera, ...);
	glTranslatef(-XCamera, -YCamera, -ZCamera);

	/* coordinate system origin - unit vectors */
	glDisable(GL_LIGHTING);
	drawCordinateSystemOrigin();
	glEnable(GL_LIGHTING);

	/* lights positioning */
	if (InteractionMode == MY_MENU_LIGHT) {
		/* move camera */
		if ((KeybModifiers & GLUT_ACTIVE_SHIFT) == 0) {
			LightPos[0] = LightPos[0] + 0.01*(GLdouble)DX;
			LightPos[1] = LightPos[1] + 0.01*(GLdouble)DY;
		}

	}
	glLightfv(GL_LIGHT0, GL_POSITION, LightPos);
	/* draw a little cube to mark the light's position: */
	glDisable(GL_LIGHTING);
	glPushMatrix();
	glTranslatef(LightPos[0], LightPos[1], LightPos[2]);
	glColor3f(1.0, 1.0, 1.0);
	glutSolidCube(0.1);
	glPopMatrix();
	glEnable(GL_LIGHTING);

	/* modeling transformations follow,  */
	/*placing 3D models in the scene */
	if (InteractionMode == MY_MENU_OBJECT) {
		/* move camera */
		if ((KeybModifiers & GLUT_ACTIVE_SHIFT) == 0) {
			TX = TX + 0.01*(GLdouble)DX;
			TY = TY + 0.01*(GLdouble)DY;
		}
	}
	/* Push/Pop Doesn't matter here due to LoadIdentity, but 
	 could matter in more complicated scene. */

	if (mouseButtons == 1) {
		glMatrixMode(GL_MODELVIEW);
		glGetFloatv(GL_MODELVIEW_MATRIX, mat);
		if (KeybModifiers & GLUT_ACTIVE_SHIFT) {
			TZ = 0.01*(GLdouble)DX + 0.01 * DY;
			TX = TY = 0;
		} else {
			TX = 0.01*(GLdouble)DX;
			TY = 0.01*(GLdouble)DY;
			TZ = 0;
		}
		glLoadIdentity();
		glTranslatef(TX, TY, TZ);
		glMultMatrixf(mat);
	}

	drawScene();

	glutSwapBuffers();
} /* myDisplay() */

/***************************************************************/
/*********************** GLUT FUNCTIONS ************************/
/***************************************************************/

/* ------------------------------------------------------------------------ */
void myReshape(int w, int h) {
	w = MY_MAX(w,128);
	h = MY_MAX(h,128);
	WindHeight = h;
	WindWidth = w;
	glutReshapeWindow(w, h);
	glViewport(0, 0, w, h);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	FOVratio = (float)w / (float)h; /* keep world coord x/y ratio == 1 */
	gluPerspective(FOVangle, FOVratio, ClipNear, ClipFar);
	glMatrixMode(GL_MODELVIEW);
} /* myReshape() */

/* ------------------------------------------------------------------------ */
void myVisibility(int state) {
	/* ------------------------------------------------------------------------ */
	if (state == GLUT_VISIBLE) {
		glutPostRedisplay();
	}
} /* myVisibility() */

/* ------------------------------------------------------------------------ */
